Micron Document
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| SparkN0de-git | SparkN0de |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


Displaying Raw • Download


meshchatx/src/backend/http/routes/app_info.py master (b89dc9e6) Text, 23.49 KB

T8b949e# SPDX-License-Identifier: 0BSD
T8b949e"""HTTP routes: app_info."""

Tff7b72from T7ee787__future__ Tff7b72import Te6edf3annotations


Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787backendT7ee787.T7ee787httpT7ee787.T7ee787meshchat_names Tff7b72import Tb4b4b4( T8b949e# noqa: F401
Te6edf3GeoValidationErrorTb4b4b4,
Te6edf3OutboundHttpBlockedErrorTb4b4b4,
Te6edf3OverlayExportErrorTb4b4b4,
Te6edf3OverlaySourceParseErrorTb4b4b4,
Te6edf3PluginSecurityErrorTb4b4b4,
Te6edf3AsyncUtilsTb4b4b4,
Te6edf3InterfaceConfigParserTb4b4b4,
Te6edf3InterfaceDiscoveryTb4b4b4,
Te6edf3InterfaceEditorTb4b4b4,
Te6edf3LOGIN_PATHTb4b4b4,
Te6edf3LXMFTb4b4b4,
Te6edf3LxmfAudioFieldTb4b4b4,
Te6edf3LxmfFileAttachmentTb4b4b4,
Te6edf3LxmfFileAttachmentsFieldTb4b4b4,
Te6edf3LxmfImageFieldTb4b4b4,
Te6edf3MAX_EXPORT_TILESTb4b4b4,
Te6edf3MarkdownRendererTb4b4b4,
Te6edf3NomadnetFileDownloaderTb4b4b4,
Te6edf3NomadnetPageDownloaderTb4b4b4,
Te6edf3RNProbeHandlerTb4b4b4,
Te6edf3RNSTb4b4b4,
Te6edf3ReticulumMeshChatTb4b4b4,
Te6edf3SETUP_PATHTb4b4b4,
Te6edf3TRANSPARENT_TILETb4b4b4,
Te6edf3TelemeterTb4b4b4,
Te6edf3UTCTb4b4b4,
Te6edf3WSMsgTypeTb4b4b4,
Te6edf3_is_chaquopy_androidTb4b4b4,
Te6edf3_is_loopback_bind_hostTb4b4b4,
Te6edf3_request_client_ipTb4b4b4,
Te6edf3aiohttpTb4b4b4,
Te6edf3app_versionTb4b4b4,
Te6edf3assert_migration_context_pathsTb4b4b4,
Te6edf3asyncioTb4b4b4,
Te6edf3base64Tb4b4b4,
Te6edf3bcryptTb4b4b4,
Te6edf3binasciiTb4b4b4,
Te6edf3build_blocklist_export_documentTb4b4b4,
Te6edf3build_export_documentTb4b4b4,
Te6edf3build_messages_export_bundleTb4b4b4,
Te6edf3cache_statsTb4b4b4,
Te6edf3cancel_inbound_deliveriesTb4b4b4,
Te6edf3castTb4b4b4,
Te6edf3compute_lxmf_conversation_unread_from_latest_rowTb4b4b4,
Te6edf3configparserTb4b4b4,
Te6edf3contextlibTb4b4b4,
Te6edf3convert_db_favourite_to_dictTb4b4b4,
Te6edf3convert_db_lxmf_message_to_dictTb4b4b4,
Te6edf3convert_lxmf_message_to_dictTb4b4b4,
Te6edf3convert_nomadnet_field_data_to_mapTb4b4b4,
Te6edf3convert_nomadnet_string_data_to_mapTb4b4b4,
Te6edf3convert_propagation_node_state_to_stringTb4b4b4,
Te6edf3copyTb4b4b4,
Te6edf3datetimeTb4b4b4,
Te6edf3describe_port_conflictTb4b4b4,
Te6edf3detect_image_format_from_magicTb4b4b4,
Te6edf3ensure_outbound_http_allowedTb4b4b4,
Te6edf3ensure_session_csrf_tokenTb4b4b4,
Te6edf3filter_announced_dicts_by_search_queryTb4b4b4,
Te6edf3fresh_storage_at_targetTb4b4b4,
Te6edf3get_cached_active_linkTb4b4b4,
Te6edf3get_file_pathTb4b4b4,
Te6edf3get_sessionTb4b4b4,
Te6edf3get_trusted_proxy_cidrsTb4b4b4,
Te6edf3gif_utilsTb4b4b4,
Te6edf3i2p_supportTb4b4b4,
Te6edf3import_messages_export_bundleTb4b4b4,
Te6edf3ioTb4b4b4,
Te6edf3is_mbtiles_filenameTb4b4b4,
Te6edf3is_path_within_dirTb4b4b4,
Te6edf3is_port_in_useTb4b4b4,
Te6edf3is_user_facing_lxmf_payloadTb4b4b4,
Te6edf3jsonTb4b4b4,
Te6edf3list_host_network_interfacesTb4b4b4,
Te6edf3list_inbound_deliveriesTb4b4b4,
Te6edf3list_portsTb4b4b4,
Te6edf3load_app_security_settingsTb4b4b4,
Te6edf3loggerTb4b4b4,
Te6edf3loggingTb4b4b4,
Te6edf3lxmf_sidebar_preview_for_conversation_latest_rowTb4b4b4,
Te6edf3memory_log_handlerTb4b4b4,
Te6edf3message_fields_have_attachmentsTb4b4b4,
Te6edf3migrate_legacy_to_targetTb4b4b4,
Te6edf3mime_for_image_typeTb4b4b4,
Te6edf3normalize_identity_storage_hashTb4b4b4,
Te6edf3normalize_lxmf_sieve_filtersTb4b4b4,
Te6edf3normalize_message_blocklistTb4b4b4,
Te6edf3osTb4b4b4,
Te6edf3parse_bool_query_paramTb4b4b4,
Te6edf3parse_import_documentTb4b4b4,
Te6edf3parse_lxmf_display_nameTb4b4b4,
Te6edf3parse_lxmf_propagation_node_app_dataTb4b4b4,
Te6edf3parse_lxmf_sieve_filters_jsonTb4b4b4,
Te6edf3parse_lxmf_stamp_costTb4b4b4,
Te6edf3parse_message_blocklist_jsonTb4b4b4,
Te6edf3parse_nomadnetwork_node_display_nameTb4b4b4,
Te6edf3platformTb4b4b4,
Te6edf3privacy_mode_enabledTb4b4b4,
Te6edf3psutilTb4b4b4,
Te6edf3purge_messages_before_cutoffTb4b4b4,
Te6edf3reTb4b4b4,
Te6edf3resolve_message_age_cutoffTb4b4b4,
Te6edf3reticulum_pathfindingTb4b4b4,
Te6edf3rotate_session_csrf_tokenTb4b4b4,
Te6edf3rrc_protocolTb4b4b4,
Te6edf3safe_path_under_dirTb4b4b4,
Te6edf3sanitize_sticker_emojiTb4b4b4,
Te6edf3sanitize_sticker_nameTb4b4b4,
Te6edf3sanitize_websocket_config_updateTb4b4b4,
Te6edf3save_app_security_settingsTb4b4b4,
Te6edf3secretsTb4b4b4,
Te6edf3shutilTb4b4b4,
Te6edf3sqlite3Tb4b4b4,
Te6edf3sticker_pack_utilsTb4b4b4,
Te6edf3sysTb4b4b4,
Te6edf3tempfileTb4b4b4,
Te6edf3threadingTb4b4b4,
Te6edf3timeTb4b4b4,
Te6edf3tracebackTb4b4b4,
Te6edf3user_agent_hashTb4b4b4,
Te6edf3validate_export_documentTb4b4b4,
Te6edf3webTb4b4b4,
Te6edf3websocket_type_requires_authTb4b4b4,
Te6edf3zipfileTb4b4b4,
Tb4b4b4)


Tff7b72def Td2a8ffregister_app_info_routesTb4b4b4(Te6edf3routesTb4b4b4, Te6edf3appTb4b4b4)Tb4b4b4:

Tf0883e@routesTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/app/sessionsTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8ffapp_sessionsTb4b4b4(Te6edf3_requestTb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Te6edf3appTff7b72.Td2a8ffget_active_sessions_payloadTb4b4b4(Tb4b4b4)Tb4b4b4)

T8b949e# get app info

T8b949e# get app info
Tf0883e@routesTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/app/infoTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8ffapp_infoTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Te6edf3process Tff7b72= Tffa657getattrTb4b4b4(Te6edf3appTb4b4b4, Ta5d6ff"Ta5d6ff_host_processTa5d6ff"Tb4b4b4, Tff7b72NoneTb4b4b4)
Tff7b72if Te6edf3process Tff7b72is Tff7b72NoneTb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3process Tff7b72= Te6edf3psutilTff7b72.Td2a8ffProcessTb4b4b4(Tb4b4b4)
Tff7b72except Tf85149ExceptionTb4b4b4:
Te6edf3process Tff7b72= Tff7b72None

Tff7b72def Td2a8ff_safe_memory_infoTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3process Tff7b72is Tff7b72NoneTb4b4b4:

Tff7b72class T56d364_EmptyMemTb4b4b4:
Te6edf3rss Tff7b72= T79c0ff0
Te6edf3vms Tff7b72= T79c0ff0

Tff7b72return Te6edf3_EmptyMemTb4b4b4(Tb4b4b4)
Tff7b72tryTb4b4b4:
Tff7b72return Te6edf3processTff7b72.Td2a8ffmemory_infoTb4b4b4(Tb4b4b4)
Tff7b72except Tf85149ExceptionTb4b4b4:

Tff7b72class T56d364_EmptyMemFallbackTb4b4b4:
Te6edf3rss Tff7b72= T79c0ff0
Te6edf3vms Tff7b72= T79c0ff0

Tff7b72return Te6edf3_EmptyMemFallbackTb4b4b4(Tb4b4b4)

Tff7b72def Td2a8ff_safe_process_usageTb4b4b4(Tb4b4b4)Tb4b4b4:
Te6edf3usageTb4b4b4: Tffa657dictTb4b4b4[Tffa657strTb4b4b4, Tffa657float Tff7b72| Tffa657int Tff7b72| Tff7b72NoneTb4b4b4] Tff7b72= Tb4b4b4{
Ta5d6ff"Ta5d6ffcpu_percentTa5d6ff"Tb4b4b4: Tff7b72NoneTb4b4b4,
Ta5d6ff"Ta5d6ffnum_threadsTa5d6ff"Tb4b4b4: Tff7b72NoneTb4b4b4,
Ta5d6ff"Ta5d6ffcreate_timeTa5d6ff"Tb4b4b4: Tff7b72NoneTb4b4b4,
Ta5d6ff"Ta5d6ffcpu_time_secondsTa5d6ff"Tb4b4b4: Tff7b72NoneTb4b4b4,
Tb4b4b4}
Tff7b72if Te6edf3process Tff7b72is Tff7b72NoneTb4b4b4:
Tff7b72return Te6edf3usage
Tff7b72tryTb4b4b4:
Te6edf3usageTb4b4b4[Ta5d6ff"Ta5d6ffcpu_percentTa5d6ff"Tb4b4b4] Tff7b72= Tffa657floatTb4b4b4(Te6edf3processTff7b72.Td2a8ffcpu_percentTb4b4b4(Te6edf3intervalTff7b72=Tff7b72NoneTb4b4b4)Tb4b4b4)
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72pass
Tff7b72tryTb4b4b4:
Te6edf3usageTb4b4b4[Ta5d6ff"Ta5d6ffnum_threadsTa5d6ff"Tb4b4b4] Tff7b72= Tffa657intTb4b4b4(Te6edf3processTff7b72.Td2a8ffnum_threadsTb4b4b4(Tb4b4b4)Tb4b4b4)
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72pass
Tff7b72tryTb4b4b4:
Te6edf3usageTb4b4b4[Ta5d6ff"Ta5d6ffcreate_timeTa5d6ff"Tb4b4b4] Tff7b72= Tffa657floatTb4b4b4(Te6edf3processTff7b72.Td2a8ffcreate_timeTb4b4b4(Tb4b4b4)Tb4b4b4)
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72pass
Tff7b72tryTb4b4b4:
Te6edf3times Tff7b72= Te6edf3processTff7b72.Td2a8ffcpu_timesTb4b4b4(Tb4b4b4)
Te6edf3usageTb4b4b4[Ta5d6ff"Ta5d6ffcpu_time_secondsTa5d6ff"Tb4b4b4] Tff7b72= Tffa657floatTb4b4b4(Te6edf3timesTff7b72.Td2a8ffuserTb4b4b4) Tff7b72+ Tffa657floatTb4b4b4(Te6edf3timesTff7b72.Td2a8ffsystemTb4b4b4)
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72pass
Tff7b72return Te6edf3usage

Tff7b72def Td2a8ff_safe_battery_usageTb4b4b4(Tb4b4b4)Tb4b4b4:
Te6edf3tracker Tff7b72= Tffa657getattrTb4b4b4(Te6edf3appTb4b4b4, Ta5d6ff"Ta5d6ffbattery_usageTa5d6ff"Tb4b4b4, Tff7b72NoneTb4b4b4)
Tff7b72if Te6edf3tracker Tff7b72is Tff7b72NoneTb4b4b4:
Tff7b72return Tff7b72None
Tff7b72tryTb4b4b4:
Tff7b72return Te6edf3trackerTff7b72.Td2a8ffsnapshotTb4b4b4(Te6edf3processTb4b4b4)
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72return Tff7b72None

Tff7b72def Td2a8ff_safe_resource_breakdownTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72tryTb4b4b4:
Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787backendT7ee787.T7ee787process_resource_breakdown Tff7b72import Tb4b4b4(
Te6edf3build_resource_breakdownTb4b4b4,
Tb4b4b4)

Tff7b72return Te6edf3build_resource_breakdownTb4b4b4(Te6edf3processTb4b4b4)
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72return Tb4b4b4[Tb4b4b4]

Tff7b72def Td2a8ff_safe_net_ioTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72tryTb4b4b4:
Tff7b72return Te6edf3psutilTff7b72.Td2a8ffnet_io_countersTb4b4b4(Tb4b4b4)
Tff7b72except Tf85149ExceptionTb4b4b4:

Tff7b72class T56d364_NTb4b4b4:
Te6edf3bytes_sent Tff7b72= T79c0ff0
Te6edf3bytes_recv Tff7b72= T79c0ff0
Te6edf3packets_sent Tff7b72= T79c0ff0
Te6edf3packets_recv Tff7b72= T79c0ff0

Tff7b72return Te6edf3_NTb4b4b4(Tb4b4b4)

T8b949e# psutil often raises on Android (restricted /proc), so never fail the whole payload.
Te6edf3memory_info Tff7b72= Te6edf3_safe_memory_infoTb4b4b4(Tb4b4b4)
Te6edf3process_usage Tff7b72= Te6edf3_safe_process_usageTb4b4b4(Tb4b4b4)
Te6edf3battery_usage Tff7b72= Te6edf3_safe_battery_usageTb4b4b4(Tb4b4b4)
Te6edf3resource_breakdown Tff7b72= Te6edf3_safe_resource_breakdownTb4b4b4(Tb4b4b4)
Te6edf3net_io Tff7b72= Te6edf3_safe_net_ioTb4b4b4(Tb4b4b4)

Tff7b72def Td2a8ff_safe_database_pathTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3appTff7b72.Td2a8ffdatabase_pathTb4b4b4:
Tff7b72return Te6edf3appTff7b72.Td2a8ffdatabase_path
Tff7b72tryTb4b4b4:
Tff7b72if Te6edf3appTff7b72.Td2a8ffdatabase Tff7b72is Tff7b72not Tff7b72None Tff7b72and Te6edf3appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffprovider Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Tff7b72return Te6edf3appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ffproviderTff7b72.Td2a8ffdb_path
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72pass
Tff7b72return Tff7b72None

Tff7b72def Td2a8ff_safe_sqlite_pragmaTb4b4b4(Te6edf3nameTb4b4b4, Te6edf3defaultTff7b72=Tff7b72NoneTb4b4b4)Tb4b4b4:
Tff7b72tryTb4b4b4:
Tff7b72if Te6edf3appTff7b72.Td2a8ffdatabase Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Tff7b72return Te6edf3appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ff_get_pragma_valueTb4b4b4(Te6edf3nameTb4b4b4, Te6edf3defaultTb4b4b4)
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72pass
Tff7b72return Te6edf3default

Tff7b72def Td2a8ff_safe_config_getTb4b4b4(Te6edf3nameTb4b4b4, Te6edf3defaultTb4b4b4)Tb4b4b4:
Tff7b72tryTb4b4b4:
Tff7b72if Te6edf3appTff7b72.Td2a8ffconfig Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Tff7b72return Te6edf3appTff7b72.Td2a8ffconfigTff7b72.Td2a8ffgetTb4b4b4(Te6edf3nameTb4b4b4, Te6edf3defaultTb4b4b4)
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72pass
Tff7b72return Te6edf3default

Tff7b72def Td2a8ff_safe_user_guidanceTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3guidance Tff7b72= Te6edf3appTff7b72.Td2a8ffbuild_user_guidance_messagesTb4b4b4(Tb4b4b4)
Tff7b72if Tffa657isinstanceTb4b4b4(Te6edf3guidanceTb4b4b4, Tffa657listTb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3guidance
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72pass
Tff7b72return Tb4b4b4[Tb4b4b4]

T8b949e# Get total paths
Te6edf3total_paths Tff7b72= T79c0ff0
Te6edf3is_connected_to_shared_instance Tff7b72= Tff7b72False
Te6edf3shared_instance_address Tff7b72= Tff7b72None
Tff7b72if Tffa657hasattrTb4b4b4(Te6edf3appTb4b4b4, Ta5d6ff"Ta5d6ffreticulumTa5d6ff"Tb4b4b4) Tff7b72and Te6edf3appTff7b72.Td2a8ffreticulumTb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3path_table Tff7b72= Te6edf3appTff7b72.Td2a8ffreticulumTff7b72.Td2a8ffget_path_tableTb4b4b4(Tb4b4b4)
Te6edf3total_paths Tff7b72= Tffa657lenTb4b4b4(Te6edf3path_tableTb4b4b4)
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72pass

Te6edf3is_connected_to_shared_instance Tff7b72= Tffa657getattrTb4b4b4(
Te6edf3appTff7b72.Td2a8ffreticulumTb4b4b4,
Ta5d6ff"Ta5d6ffis_connected_to_shared_instanceTa5d6ff"Tb4b4b4,
Tff7b72FalseTb4b4b4,
Tb4b4b4)

Tff7b72if Te6edf3is_connected_to_shared_instanceTb4b4b4:
T8b949e# Try to find the shared instance address from active connections
Tff7b72tryTb4b4b4:
Tff7b72if Te6edf3process Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4:
Tff7b72for Te6edf3conn Tff7b72in Te6edf3processTff7b72.Td2a8ffnet_connectionsTb4b4b4(Te6edf3kindTff7b72=Ta5d6ff"Ta5d6ffallTa5d6ff"Tb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3connTff7b72.Td2a8ffstatus Tff7b72== Te6edf3psutilTff7b72.Td2a8ffCONN_ESTABLISHED Tff7b72and Te6edf3connTff7b72.Td2a8ffraddrTb4b4b4:
T8b949e# Check for common Reticulum shared instance ports or UNIX sockets
Tff7b72if Tb4b4b4(
Tffa657isinstanceTb4b4b4(Te6edf3connTff7b72.Td2a8ffraddrTb4b4b4, Tffa657tupleTb4b4b4)
Tff7b72and Te6edf3connTff7b72.Td2a8ffraddrTb4b4b4[T79c0ff1Tb4b4b4] Tff7b72== T79c0ff37428
Tb4b4b4)Tb4b4b4:
Te6edf3shared_instance_address Tff7b72= Tb4b4b4(
Ta5d6fffTa5d6ff"Tffd700{Te6edf3connTff7b72.Td2a8ffraddrTb4b4b4[T79c0ff0Tb4b4b4]Tffd700}Ta5d6ff:Tffd700{Te6edf3connTff7b72.Td2a8ffraddrTb4b4b4[T79c0ff1Tb4b4b4]Tffd700}Ta5d6ff"
Tb4b4b4)
Tff7b72break
Tff7b72if Tb4b4b4(
Tffa657isinstanceTb4b4b4(Te6edf3connTff7b72.Td2a8ffraddrTb4b4b4, Tffa657strTb4b4b4)
Tff7b72and Tb4b4b4(
Ta5d6ff"Ta5d6ffrnsTa5d6ff" Tff7b72in Te6edf3connTff7b72.Td2a8ffraddr Tff7b72or Ta5d6ff"Ta5d6ffreticulumTa5d6ff" Tff7b72in Te6edf3connTff7b72.Td2a8ffraddr
Tb4b4b4)
Tff7b72and Ta5d6ff"Ta5d6ff.sockTa5d6ff" Tff7b72in Te6edf3connTff7b72.Td2a8ffraddr
Tb4b4b4)Tb4b4b4:
Te6edf3shared_instance_address Tff7b72= Te6edf3connTff7b72.Td2a8ffraddr
Tff7b72break
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72pass

T8b949e# Fallback to reading config if not found via connections
Tff7b72if Tff7b72not Te6edf3shared_instance_addressTb4b4b4:
Tff7b72tryTb4b4b4:
Te6edf3config_dir Tff7b72= Te6edf3appTff7b72.Td2a8ff_normalize_reticulum_config_dirTb4b4b4(
Tffa657getattrTb4b4b4(Te6edf3appTb4b4b4, Ta5d6ff"Ta5d6ffreticulum_config_dirTa5d6ff"Tb4b4b4, Tff7b72NoneTb4b4b4)Tb4b4b4,
Tb4b4b4)

Te6edf3config_path Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3config_dirTb4b4b4, Ta5d6ff"Ta5d6ffconfigTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffisfileTb4b4b4(Te6edf3config_pathTb4b4b4)Tb4b4b4:
Te6edf3cp Tff7b72= Te6edf3configparserTff7b72.Td2a8ffConfigParserTb4b4b4(Tb4b4b4)
Tff7b72tryTb4b4b4:
Te6edf3cpTff7b72.Td2a8ffreadTb4b4b4(Te6edf3config_pathTb4b4b4)
Tff7b72except Te6edf3configparserTff7b72.Td2a8ffErrorTb4b4b4:
Tff7b72pass
Tff7b72if Te6edf3cpTff7b72.Td2a8ffhas_sectionTb4b4b4(Ta5d6ff"Ta5d6ffreticulumTa5d6ff"Tb4b4b4)Tb4b4b4:
Te6edf3shared_port Tff7b72= Te6edf3cpTff7b72.Td2a8ffgetintTb4b4b4(
Ta5d6ff"Ta5d6ffreticulumTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffshared_instance_portTa5d6ff"Tb4b4b4,
Te6edf3fallbackTff7b72=T79c0ff37428Tb4b4b4,
Tb4b4b4)
Te6edf3shared_bind Tff7b72= Te6edf3cpTff7b72.Td2a8ffgetTb4b4b4(
Ta5d6ff"Ta5d6ffreticulumTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffshared_instance_bindTa5d6ff"Tb4b4b4,
Te6edf3fallbackTff7b72=Ta5d6ff"Ta5d6ff127.0.0.1Ta5d6ff"Tb4b4b4,
Tb4b4b4)
Te6edf3shared_instance_address Tff7b72= Ta5d6fffTa5d6ff"Tffd700{Te6edf3shared_bindTffd700}Ta5d6ff:Tffd700{Te6edf3shared_portTffd700}Ta5d6ff"
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72pass

T8b949e# Calculate announce rates
Te6edf3current_time Tff7b72= Te6edf3timeTff7b72.Td2a8fftimeTb4b4b4(Tb4b4b4)
Te6edf3announces_per_second Tff7b72= Tffa657lenTb4b4b4(
Tb4b4b4[Te6edf3t Tff7b72for Te6edf3t Tff7b72in Te6edf3appTff7b72.Td2a8ffannounce_timestamps Tff7b72if Te6edf3current_time Tff7b72- Te6edf3t Tff7b72<Tff7b72= T79c0ff1.0Tb4b4b4]Tb4b4b4,
Tb4b4b4)
Te6edf3announces_per_minute Tff7b72= Tffa657lenTb4b4b4(
Tb4b4b4[Te6edf3t Tff7b72for Te6edf3t Tff7b72in Te6edf3appTff7b72.Td2a8ffannounce_timestamps Tff7b72if Te6edf3current_time Tff7b72- Te6edf3t Tff7b72<Tff7b72= T79c0ff60.0Tb4b4b4]Tb4b4b4,
Tb4b4b4)
Te6edf3announces_per_hour Tff7b72= Tffa657lenTb4b4b4(
Tb4b4b4[Te6edf3t Tff7b72for Te6edf3t Tff7b72in Te6edf3appTff7b72.Td2a8ffannounce_timestamps Tff7b72if Te6edf3current_time Tff7b72- Te6edf3t Tff7b72<Tff7b72= T79c0ff3600.0Tb4b4b4]Tb4b4b4,
Tb4b4b4)

T8b949e# Clean up old announce timestamps (older than 1 hour)
Te6edf3appTff7b72.Td2a8ffannounce_timestamps Tff7b72= Tb4b4b4[
Te6edf3t Tff7b72for Te6edf3t Tff7b72in Te6edf3appTff7b72.Td2a8ffannounce_timestamps Tff7b72if Te6edf3current_time Tff7b72- Te6edf3t Tff7b72<Tff7b72= T79c0ff3600.0
Tb4b4b4]

T8b949e# Calculate average download speed
Te6edf3avg_download_speed_bps Tff7b72= Tff7b72None
Tff7b72if Te6edf3appTff7b72.Td2a8ffdownload_speedsTb4b4b4:
Te6edf3total_bytes Tff7b72= Tffa657sumTb4b4b4(Te6edf3size Tff7b72for Te6edf3sizeTb4b4b4, Te6edf3_ Tff7b72in Te6edf3appTff7b72.Td2a8ffdownload_speedsTb4b4b4)
Te6edf3total_duration Tff7b72= Tffa657sumTb4b4b4(Te6edf3duration Tff7b72for Te6edf3_Tb4b4b4, Te6edf3duration Tff7b72in Te6edf3appTff7b72.Td2a8ffdownload_speedsTb4b4b4)
Tff7b72if Te6edf3total_duration Tff7b72> T79c0ff0Tb4b4b4:
Te6edf3avg_download_speed_bps Tff7b72= Te6edf3total_bytes Tff7b72/ Te6edf3total_duration

Tff7b72tryTb4b4b4:
Te6edf3db_files Tff7b72= Tb4b4b4(
Te6edf3appTff7b72.Td2a8ffdatabaseTff7b72.Td2a8ff_get_database_file_statsTb4b4b4(Tb4b4b4)
Tff7b72if Te6edf3appTff7b72.Td2a8ffdatabase Tff7b72is Tff7b72not Tff7b72None
Tff7b72else Tb4b4b4{
Ta5d6ff"Ta5d6ffmain_bytesTa5d6ff"Tb4b4b4: T79c0ff0Tb4b4b4,
Ta5d6ff"Ta5d6ffwal_bytesTa5d6ff"Tb4b4b4: T79c0ff0Tb4b4b4,
Ta5d6ff"Ta5d6ffshm_bytesTa5d6ff"Tb4b4b4: T79c0ff0Tb4b4b4,
Ta5d6ff"Ta5d6fftotal_bytesTa5d6ff"Tb4b4b4: T79c0ff0Tb4b4b4,
Tb4b4b4}
Tb4b4b4)
Tff7b72except Tf85149ExceptionTb4b4b4:
Te6edf3db_files Tff7b72= Tb4b4b4{
Ta5d6ff"Ta5d6ffmain_bytesTa5d6ff"Tb4b4b4: T79c0ff0Tb4b4b4,
Ta5d6ff"Ta5d6ffwal_bytesTa5d6ff"Tb4b4b4: T79c0ff0Tb4b4b4,
Ta5d6ff"Ta5d6ffshm_bytesTa5d6ff"Tb4b4b4: T79c0ff0Tb4b4b4,
Ta5d6ff"Ta5d6fftotal_bytesTa5d6ff"Tb4b4b4: T79c0ff0Tb4b4b4,
Tb4b4b4}

Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffapp_infoTa5d6ff"Tb4b4b4: Tb4b4b4{
Ta5d6ff"Ta5d6ffversionTa5d6ff"Tb4b4b4: Te6edf3appTff7b72.Td2a8ffget_app_versionTb4b4b4(Tb4b4b4)Tb4b4b4,
Tff7b72*Tff7b72*Tb4b4b4(Te6edf3appTff7b72.Td2a8ffget_build_metaTb4b4b4(Tb4b4b4) Tff7b72if Tffa657hasattrTb4b4b4(Te6edf3appTb4b4b4, Ta5d6ff"Ta5d6ffget_build_metaTa5d6ff"Tb4b4b4) Tff7b72else Tb4b4b4{Tb4b4b4}Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6fflxmf_versionTa5d6ff"Tb4b4b4: Te6edf3LXMFTff7b72.Td2a8ff__version__Tb4b4b4,
Ta5d6ff"Ta5d6ffrns_versionTa5d6ff"Tb4b4b4: Te6edf3RNSTff7b72.Td2a8ff__version__Tb4b4b4,
Ta5d6ff"Ta5d6fflxst_versionTa5d6ff"Tb4b4b4: Te6edf3appTff7b72.Td2a8ffget_lxst_versionTb4b4b4(Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffpython_versionTa5d6ff"Tb4b4b4: Te6edf3platformTff7b72.Td2a8ffpython_versionTb4b4b4(Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffdependenciesTa5d6ff"Tb4b4b4: Tb4b4b4{
Ta5d6ff"Ta5d6ffaiohttpTa5d6ff"Tb4b4b4: Te6edf3appTff7b72.Td2a8ffget_package_versionTb4b4b4(Ta5d6ff"Ta5d6ffaiohttpTa5d6ff"Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffaiohttp_sessionTa5d6ff"Tb4b4b4: Te6edf3appTff7b72.Td2a8ffget_package_versionTb4b4b4(
Ta5d6ff"Ta5d6ffaiohttp-sessionTa5d6ff"Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffcryptographyTa5d6ff"Tb4b4b4: Te6edf3appTff7b72.Td2a8ffget_package_versionTb4b4b4(Ta5d6ff"Ta5d6ffcryptographyTa5d6ff"Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffpsutilTa5d6ff"Tb4b4b4: Te6edf3appTff7b72.Td2a8ffget_package_versionTb4b4b4(Ta5d6ff"Ta5d6ffpsutilTa5d6ff"Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffwebsocketsTa5d6ff"Tb4b4b4: Te6edf3appTff7b72.Td2a8ffget_package_versionTb4b4b4(Ta5d6ff"Ta5d6ffwebsocketsTa5d6ff"Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffaudioop_ltsTa5d6ff"Tb4b4b4: Tb4b4b4(
Te6edf3appTff7b72.Td2a8ffget_package_versionTb4b4b4(Ta5d6ff"Ta5d6ffaudioop-ltsTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3sysTff7b72.Td2a8ffversion_info Tff7b72>Tff7b72= Tb4b4b4(T79c0ff3Tb4b4b4, T79c0ff13Tb4b4b4)
Tff7b72else Ta5d6ff"Ta5d6ffn/aTa5d6ff"
Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffplyTa5d6ff"Tb4b4b4: Te6edf3appTff7b72.Td2a8ffget_package_versionTb4b4b4(Ta5d6ff"Ta5d6ffplyTa5d6ff"Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffbcryptTa5d6ff"Tb4b4b4: Te6edf3appTff7b72.Td2a8ffget_package_versionTb4b4b4(Ta5d6ff"Ta5d6ffbcryptTa5d6ff"Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6fflxmfyTa5d6ff"Tb4b4b4: Te6edf3appTff7b72.Td2a8ffget_package_versionTb4b4b4(Ta5d6ff"Ta5d6fflxmfyTa5d6ff"Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffrns_filesyncTa5d6ff"Tb4b4b4: Te6edf3appTff7b72.Td2a8ffget_package_versionTb4b4b4(Ta5d6ff"Ta5d6ffrns-filesyncTa5d6ff"Tb4b4b4)Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Ta5d6ff"Ta5d6ffstorage_pathTa5d6ff"Tb4b4b4: Te6edf3appTff7b72.Td2a8ffstorage_pathTb4b4b4,
Ta5d6ff"Ta5d6ffdatabase_pathTa5d6ff"Tb4b4b4: Te6edf3_safe_database_pathTb4b4b4(Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffdatabase_file_sizeTa5d6ff"Tb4b4b4: Te6edf3db_filesTb4b4b4[Ta5d6ff"Ta5d6ffmain_bytesTa5d6ff"Tb4b4b4]Tb4b4b4,
Ta5d6ff"Ta5d6ffdatabase_filesTa5d6ff"Tb4b4b4: Te6edf3db_filesTb4b4b4,
Ta5d6ff"Ta5d6ffsqliteTa5d6ff"Tb4b4b4: Tb4b4b4{
Ta5d6ff"Ta5d6ffjournal_modeTa5d6ff"Tb4b4b4: Te6edf3_safe_sqlite_pragmaTb4b4b4(
Ta5d6ff"Ta5d6ffjournal_modeTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffunknownTa5d6ff"Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffsynchronousTa5d6ff"Tb4b4b4: Te6edf3_safe_sqlite_pragmaTb4b4b4(Ta5d6ff"Ta5d6ffsynchronousTa5d6ff"Tb4b4b4, Tff7b72NoneTb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffwal_autocheckpointTa5d6ff"Tb4b4b4: Te6edf3_safe_sqlite_pragmaTb4b4b4(
Ta5d6ff"Ta5d6ffwal_autocheckpointTa5d6ff"Tb4b4b4,
Tff7b72NoneTb4b4b4,
Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffbusy_timeoutTa5d6ff"Tb4b4b4: Te6edf3_safe_sqlite_pragmaTb4b4b4(Ta5d6ff"Ta5d6ffbusy_timeoutTa5d6ff"Tb4b4b4, Tff7b72NoneTb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6fftemp_storeTa5d6ff"Tb4b4b4: Te6edf3_safe_sqlite_pragmaTb4b4b4(Ta5d6ff"Ta5d6fftemp_storeTa5d6ff"Tb4b4b4, Tff7b72NoneTb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffcache_sizeTa5d6ff"Tb4b4b4: Te6edf3_safe_sqlite_pragmaTb4b4b4(Ta5d6ff"Ta5d6ffcache_sizeTa5d6ff"Tb4b4b4, Tff7b72NoneTb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffmmap_sizeTa5d6ff"Tb4b4b4: Te6edf3_safe_sqlite_pragmaTb4b4b4(Ta5d6ff"Ta5d6ffmmap_sizeTa5d6ff"Tb4b4b4, Tff7b72NoneTb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffmemory_relaxedTa5d6ff"Tb4b4b4: Tffa657boolTb4b4b4(
Tffa657getattrTb4b4b4(
Te6edf3appTff7b72.Td2a8ffdatabaseTb4b4b4,
Ta5d6ff"Ta5d6ff_sqlite_memory_relaxedTa5d6ff"Tb4b4b4,
Tff7b72FalseTb4b4b4,
Tb4b4b4)
Tff7b72if Te6edf3appTff7b72.Td2a8ffdatabase Tff7b72is Tff7b72not Tff7b72None
Tff7b72else Tff7b72FalseTb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Ta5d6ff"Ta5d6ffreticulum_config_pathTa5d6ff"Tb4b4b4: Te6edf3appTff7b72.Td2a8ff_api_reticulum_config_pathTb4b4b4(Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffhost_platformTa5d6ff"Tb4b4b4: Te6edf3sysTff7b72.Td2a8ffplatformTb4b4b4,
Tff7b72*Tff7b72*Te6edf3appTff7b72.Td2a8ff_landlock_status_dictTb4b4b4(Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffis_connected_to_shared_instanceTa5d6ff"Tb4b4b4: Te6edf3is_connected_to_shared_instanceTb4b4b4,
Ta5d6ff"Ta5d6ffshared_instance_addressTa5d6ff"Tb4b4b4: Te6edf3shared_instance_addressTb4b4b4,
Ta5d6ff"Ta5d6ffis_transport_enabledTa5d6ff"Tb4b4b4: Tb4b4b4(
Te6edf3appTff7b72.Td2a8ffreticulumTff7b72.Td2a8fftransport_enabledTb4b4b4(Tb4b4b4)
Tff7b72if Tffa657hasattrTb4b4b4(Te6edf3appTb4b4b4, Ta5d6ff"Ta5d6ffreticulumTa5d6ff"Tb4b4b4) Tff7b72and Te6edf3appTff7b72.Td2a8ffreticulum
Tff7b72else Tff7b72False
Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffmemory_usageTa5d6ff"Tb4b4b4: Tb4b4b4{
Ta5d6ff"Ta5d6ffrssTa5d6ff"Tb4b4b4: Te6edf3memory_infoTff7b72.Td2a8ffrssTb4b4b4,
Ta5d6ff"Ta5d6ffvmsTa5d6ff"Tb4b4b4: Te6edf3memory_infoTff7b72.Td2a8ffvmsTb4b4b4,
Ta5d6ff"Ta5d6ffcpu_percentTa5d6ff"Tb4b4b4: Te6edf3process_usageTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffcpu_percentTa5d6ff"Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffnum_threadsTa5d6ff"Tb4b4b4: Te6edf3process_usageTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffnum_threadsTa5d6ff"Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffcreate_timeTa5d6ff"Tb4b4b4: Te6edf3process_usageTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffcreate_timeTa5d6ff"Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffcpu_time_secondsTa5d6ff"Tb4b4b4: Te6edf3process_usageTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffcpu_time_secondsTa5d6ff"Tb4b4b4)Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Ta5d6ff"Ta5d6ffresource_breakdownTa5d6ff"Tb4b4b4: Te6edf3resource_breakdownTb4b4b4,
Ta5d6ff"Ta5d6ffbattery_usageTa5d6ff"Tb4b4b4: Te6edf3battery_usageTb4b4b4,
Ta5d6ff"Ta5d6ffnetwork_statsTa5d6ff"Tb4b4b4: Tb4b4b4{
Ta5d6ff"Ta5d6ffbytes_sentTa5d6ff"Tb4b4b4: Te6edf3net_ioTff7b72.Td2a8ffbytes_sentTb4b4b4,
Ta5d6ff"Ta5d6ffbytes_recvTa5d6ff"Tb4b4b4: Te6edf3net_ioTff7b72.Td2a8ffbytes_recvTb4b4b4,
Ta5d6ff"Ta5d6ffpackets_sentTa5d6ff"Tb4b4b4: Te6edf3net_ioTff7b72.Td2a8ffpackets_sentTb4b4b4,
Ta5d6ff"Ta5d6ffpackets_recvTa5d6ff"Tb4b4b4: Te6edf3net_ioTff7b72.Td2a8ffpackets_recvTb4b4b4,
Tb4b4b4}Tb4b4b4,
Ta5d6ff"Ta5d6ffreticulum_statsTa5d6ff"Tb4b4b4: Tb4b4b4{
Ta5d6ff"Ta5d6fftotal_pathsTa5d6ff"Tb4b4b4: Te6edf3total_pathsTb4b4b4,
Ta5d6ff"Ta5d6ffannounces_per_secondTa5d6ff"Tb4b4b4: Te6edf3announces_per_secondTb4b4b4,
Ta5d6ff"Ta5d6ffannounces_per_minuteTa5d6ff"Tb4b4b4: Te6edf3announces_per_minuteTb4b4b4,
Ta5d6ff"Ta5d6ffannounces_per_hourTa5d6ff"Tb4b4b4: Te6edf3announces_per_hourTb4b4b4,
Tff7b72*Tff7b72*Te6edf3cache_statsTb4b4b4(Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffmemory_cleanupTa5d6ff"Tb4b4b4: Tffa657getattrTb4b4b4(
Tffa657getattrTb4b4b4(Te6edf3appTb4b4b4, Ta5d6ff"Ta5d6ffmemory_pressureTa5d6ff"Tb4b4b4, Tff7b72NoneTb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6fflast_statsTa5d6ff"Tb4b4b4,
Tb4b4b4{Tb4b4b4}Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Ta5d6ff"Ta5d6ffis_reticulum_runningTa5d6ff"Tb4b4b4: Tffa657hasattrTb4b4b4(Te6edf3appTb4b4b4, Ta5d6ff"Ta5d6ffreticulumTa5d6ff"Tb4b4b4)
Tff7b72and Te6edf3appTff7b72.Td2a8ffreticulum Tff7b72is Tff7b72not Tff7b72NoneTb4b4b4,
Ta5d6ff"Ta5d6ffdownload_statsTa5d6ff"Tb4b4b4: Tb4b4b4{
Ta5d6ff"Ta5d6ffavg_download_speed_bpsTa5d6ff"Tb4b4b4: Te6edf3avg_download_speed_bpsTb4b4b4,
Tb4b4b4}Tb4b4b4,
Ta5d6ff"Ta5d6ffemergencyTa5d6ff"Tb4b4b4: Tffa657getattrTb4b4b4(Te6edf3appTb4b4b4, Ta5d6ff"Ta5d6ffemergencyTa5d6ff"Tb4b4b4, Tff7b72FalseTb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffintegrity_issuesTa5d6ff"Tb4b4b4: Tffa657getattrTb4b4b4(Te6edf3appTb4b4b4, Ta5d6ff"Ta5d6ffintegrity_issuesTa5d6ff"Tb4b4b4, Tb4b4b4[Tb4b4b4]Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffdatabase_health_issuesTa5d6ff"Tb4b4b4: Tffa657getattrTb4b4b4(
Te6edf3appTb4b4b4,
Ta5d6ff"Ta5d6ffdatabase_health_issuesTa5d6ff"Tb4b4b4,
Tb4b4b4[Tb4b4b4]Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffuser_guidanceTa5d6ff"Tb4b4b4: Te6edf3_safe_user_guidanceTb4b4b4(Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6fftutorial_seenTa5d6ff"Tb4b4b4: Te6edf3_safe_config_getTb4b4b4(Ta5d6ff"Ta5d6fftutorial_seenTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6fffalseTa5d6ff"Tb4b4b4)
Tff7b72== Ta5d6ff"Ta5d6fftrueTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffchangelog_seen_versionTa5d6ff"Tb4b4b4: Te6edf3_safe_config_getTb4b4b4(
Ta5d6ff"Ta5d6ffchangelog_seen_versionTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff0.0.0Ta5d6ff"Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffmigrationTa5d6ff"Tb4b4b4: Tffa657dictTb4b4b4(Te6edf3appTff7b72.Td2a8ffmigration_contextTb4b4b4)Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)

T8b949e# get changelog

T8b949e# get changelog
Tf0883e@routesTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/app/changelogTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8ffapp_changelogTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Te6edf3changelog_path Tff7b72= Te6edf3get_file_pathTb4b4b4(Ta5d6ff"Ta5d6ffCHANGELOG.mdTa5d6ff"Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexistsTb4b4b4(Te6edf3changelog_pathTb4b4b4)Tb4b4b4:
T8b949e# try in public folder
Te6edf3changelog_path Tff7b72= Te6edf3get_file_pathTb4b4b4(Ta5d6ff"Ta5d6ffpublic/CHANGELOG.mdTa5d6ff"Tb4b4b4)

Tff7b72if Tff7b72not Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexistsTb4b4b4(Te6edf3changelog_pathTb4b4b4)Tb4b4b4:
T8b949e# try project root if not found in package
Te6edf3changelog_path Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(
Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffdirnameTb4b4b4(Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffdirnameTb4b4b4(Tff7b72__file__Tb4b4b4)Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ffCHANGELOG.mdTa5d6ff"Tb4b4b4,
Tb4b4b4)

Tff7b72if Tff7b72not Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexistsTb4b4b4(Te6edf3changelog_pathTb4b4b4)Tb4b4b4:
Te6edf3fallback_markdown Tff7b72= Tb4b4b4(
Ta5d6fffTa5d6ff"Ta5d6ff# MeshChatX Tffd700{Te6edf3app_versionTffd700}Tffea00\nTffea00\nTa5d6ff"
Ta5d6ff"Ta5d6ffChangelog is unavailable in this build.Tffea00\nTffea00\nTa5d6ff"
Ta5d6ff"Ta5d6ffPlease check the project release page for full notes.Ta5d6ff"
Tb4b4b4)
Te6edf3html_content Tff7b72= Te6edf3MarkdownRendererTff7b72.Td2a8ffrenderTb4b4b4(Te6edf3fallback_markdownTb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffchangelogTa5d6ff"Tb4b4b4: Te6edf3fallback_markdownTb4b4b4,
Ta5d6ff"Ta5d6ffhtmlTa5d6ff"Tb4b4b4: Te6edf3html_contentTb4b4b4,
Ta5d6ff"Ta5d6ffversionTa5d6ff"Tb4b4b4: Te6edf3app_versionTb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)

Tff7b72tryTb4b4b4:
Tff7b72with Tffa657openTb4b4b4(Te6edf3changelog_pathTb4b4b4) Tff7b72as Te6edf3fTb4b4b4:
Te6edf3content Tff7b72= Te6edf3fTff7b72.Td2a8ffreadTb4b4b4(Tb4b4b4)

T8b949e# Render markdown to HTML
Te6edf3html_content Tff7b72= Te6edf3MarkdownRendererTff7b72.Td2a8ffrenderTb4b4b4(Te6edf3contentTb4b4b4)

Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffchangelogTa5d6ff"Tb4b4b4: Te6edf3contentTb4b4b4,
Ta5d6ff"Ta5d6ffhtmlTa5d6ff"Tb4b4b4: Te6edf3html_contentTb4b4b4,
Ta5d6ff"Ta5d6ffversionTa5d6ff"Tb4b4b4: Te6edf3app_versionTb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6fferrorTa5d6ff"Tb4b4b4: Tffa657strTb4b4b4(Te6edf3eTb4b4b4)Tb4b4b4}Tb4b4b4, Te6edf3statusTff7b72=T79c0ff500Tb4b4b4)

T8b949e# third-party dependency licenses (Python + Node)

T8b949e# third-party dependency licenses (Python + Node)
Tf0883e@routesTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/licensesTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8fflicenses_listTb4b4b4(Te6edf3_requestTb4b4b4)Tb4b4b4:
Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787backendT7ee787.T7ee787licenses_collector Tff7b72import Te6edf3build_licenses_payload

Tff7b72tryTb4b4b4:
Te6edf3payload Tff7b72= Tff7b72await Te6edf3asyncioTff7b72.Td2a8ffto_threadTb4b4b4(Te6edf3build_licenses_payloadTb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Te6edf3payloadTb4b4b4)
Tff7b72except Tf85149Exception Tff7b72as Te6edf3eTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6fferrorTa5d6ff"Tb4b4b4: Tffa657strTb4b4b4(Te6edf3eTb4b4b4)Tb4b4b4}Tb4b4b4, Te6edf3statusTff7b72=T79c0ff500Tb4b4b4)

T8b949e# mark tutorial as seen

T8b949e# mark tutorial as seen
Tf0883e@routesTff7b72.Td2a8ffpostTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/app/tutorial/seenTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8ffapp_tutorial_seenTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Te6edf3appTff7b72.Td2a8ffconfigTff7b72.Td2a8ffsetTb4b4b4(Ta5d6ff"Ta5d6fftutorial_seenTa5d6ff"Tb4b4b4, Tff7b72TrueTb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffTutorial marked as seenTa5d6ff"Tb4b4b4}Tb4b4b4)

Tf0883e@routesTff7b72.Td2a8ffpostTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/setup/storage-migrationTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8ffsetup_storage_migrationTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Tff7b72if Tff7b72not Te6edf3appTff7b72.Td2a8ffmigration_contextTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffshow_choiceTa5d6ff"Tb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6fferrorTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffNo storage migration is pendingTa5d6ff"Tb4b4b4}Tb4b4b4,
Te6edf3statusTff7b72=T79c0ff400Tb4b4b4,
Tb4b4b4)
Tff7b72tryTb4b4b4:
Te6edf3data Tff7b72= Tff7b72await Te6edf3requestTff7b72.Td2a8ffjsonTb4b4b4(Tb4b4b4)
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6fferrorTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffInvalid JSONTa5d6ff"Tb4b4b4}Tb4b4b4, Te6edf3statusTff7b72=T79c0ff400Tb4b4b4)
Te6edf3action Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffactionTa5d6ff"Tb4b4b4)
Te6edf3leg Tff7b72= Te6edf3appTff7b72.Td2a8ffmigration_contextTb4b4b4[Ta5d6ff"Ta5d6fflegacy_pathTa5d6ff"Tb4b4b4]
Te6edf3tgt Tff7b72= Te6edf3appTff7b72.Td2a8ffmigration_contextTb4b4b4[Ta5d6ff"Ta5d6fftarget_pathTa5d6ff"Tb4b4b4]
Tff7b72tryTb4b4b4:
Te6edf3assert_migration_context_pathsTb4b4b4(Te6edf3appTff7b72.Td2a8ffmigration_contextTb4b4b4, Te6edf3legTb4b4b4, Te6edf3tgtTb4b4b4)
Tff7b72except Tf85149ValueError Tff7b72as Te6edf3eTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6fferrorTa5d6ff"Tb4b4b4: Tffa657strTb4b4b4(Te6edf3eTb4b4b4)Tb4b4b4}Tb4b4b4, Te6edf3statusTff7b72=T79c0ff400Tb4b4b4)
Tff7b72tryTb4b4b4:
Tff7b72if Te6edf3action Tff7b72== Ta5d6ff"Ta5d6ffmigrateTa5d6ff"Tb4b4b4:
Te6edf3migrate_legacy_to_targetTb4b4b4(Te6edf3legTb4b4b4, Te6edf3tgtTb4b4b4)
Tff7b72elif Te6edf3action Tff7b72== Ta5d6ff"Ta5d6fffreshTa5d6ff"Tb4b4b4:
Te6edf3fresh_storage_at_targetTb4b4b4(Te6edf3tgtTb4b4b4)
Tff7b72elseTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6fferrorTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffUnknown actionTa5d6ff"Tb4b4b4}Tb4b4b4, Te6edf3statusTff7b72=T79c0ff400Tb4b4b4)
Tff7b72except Tf85149ValueError Tff7b72as Te6edf3eTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6fferrorTa5d6ff"Tb4b4b4: Tffa657strTb4b4b4(Te6edf3eTb4b4b4)Tb4b4b4}Tb4b4b4, Te6edf3statusTff7b72=T79c0ff409Tb4b4b4)
Tff7b72except Tf85149OSError Tff7b72as Te6edf3eTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6fferrorTa5d6ff"Tb4b4b4: Tffa657strTb4b4b4(Te6edf3eTb4b4b4)Tb4b4b4}Tb4b4b4, Te6edf3statusTff7b72=T79c0ff500Tb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6ffokTa5d6ff"Tb4b4b4: Tff7b72TrueTb4b4b4, Ta5d6ff"Ta5d6ffrestart_requiredTa5d6ff"Tb4b4b4: Tff7b72TrueTb4b4b4}Tb4b4b4)

T8b949e# acknowledge and reset integrity issues

T8b949e# acknowledge and reset integrity issues
Tf0883e@routesTff7b72.Td2a8ffpostTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/app/integrity/acknowledgeTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8ffapp_integrity_acknowledgeTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Tff7b72if Te6edf3appTff7b72.Td2a8ffcurrent_contextTb4b4b4:
Te6edf3appTff7b72.Td2a8ffcurrent_contextTff7b72.Td2a8ffintegrity_managerTff7b72.Td2a8ffsave_manifestTb4b4b4(Tb4b4b4)
Te6edf3appTff7b72.Td2a8ffintegrity_issues Tff7b72= Tb4b4b4[Tb4b4b4]
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffIntegrity issues acknowledged and manifest resetTa5d6ff"Tb4b4b4}Tb4b4b4,
Tb4b4b4)

T8b949e# mark changelog as seen

T8b949e# mark changelog as seen
Tf0883e@routesTff7b72.Td2a8ffpostTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/app/changelog/seenTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8ffapp_changelog_seenTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
Te6edf3data Tff7b72= Tff7b72await Te6edf3requestTff7b72.Td2a8ffjsonTb4b4b4(Tb4b4b4)
Te6edf3version Tff7b72= Te6edf3dataTff7b72.Td2a8ffgetTb4b4b4(Ta5d6ff"Ta5d6ffversionTa5d6ff"Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3versionTb4b4b4:
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6fferrorTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffVersion requiredTa5d6ff"Tb4b4b4}Tb4b4b4, Te6edf3statusTff7b72=T79c0ff400Tb4b4b4)

Te6edf3appTff7b72.Td2a8ffconfigTff7b72.Td2a8ffsetTb4b4b4(Ta5d6ff"Ta5d6ffchangelog_seen_versionTa5d6ff"Tb4b4b4, Te6edf3versionTb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(
Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6fffTa5d6ff"Ta5d6ffChangelog version Tffd700{Te6edf3versionTffd700}Ta5d6ff marked as seenTa5d6ff"Tb4b4b4}Tb4b4b4,
Tb4b4b4)

T8b949e# shutdown app

T8b949e# shutdown app
Tf0883e@routesTff7b72.Td2a8ffpostTb4b4b4(Ta5d6ff"Ta5d6ff/api/v1/app/shutdownTa5d6ff"Tb4b4b4)
Tff7b72async Tff7b72def Td2a8ffapp_shutdownTb4b4b4(Te6edf3requestTb4b4b4)Tb4b4b4:
T8b949e# perform shutdown in a separate task so we can respond to the request
Tff7b72async Tff7b72def Td2a8ffdo_shutdownTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72await Te6edf3asyncioTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff0.5Tb4b4b4) T8b949e# give some time for the response to be sent
Tff7b72await Te6edf3appTff7b72.Td2a8ffshutdownTb4b4b4(Tff7b72NoneTb4b4b4)
Te6edf3appTff7b72.Td2a8ffexit_appTb4b4b4(T79c0ff0Tb4b4b4)

Te6edf3asyncioTff7b72.Td2a8ffcreate_taskTb4b4b4(Te6edf3do_shutdownTb4b4b4(Tb4b4b4)Tb4b4b4)
Tff7b72return Te6edf3webTff7b72.Td2a8ffjson_responseTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6ffmessageTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffShutting down...Ta5d6ff"Tb4b4b4}Tb4b4b4)

T8b949e# get docs status


──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────